﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using undbx.net;

namespace UnDbxNET.Files
{
    public delegate void PassLogMessageEventHandler(string e);
    public delegate void PassProgressEventHandler(int e);
    public delegate void PassProcessFinishedEventHandler();

    class dbxFile
    {
        public event PassLogMessageEventHandler PassLogMessage;
        public event PassProgressEventHandler PassProgress;
        public event PassProcessFinishedEventHandler PassProcessFinished;

        const int INDEX_POINTER = 0xE4;
        const int ITEM_COUNT = 0xC4;
        const int DBX_MAX_FILENAME = 128;
        public dbxFile(FileInfo pMyDbxFile, bool pRecover, PassLogMessageEventHandler pHandlerLogMsg = null,
            PassProgressEventHandler pHandlerProgress = null, PassProcessFinishedEventHandler pHandlerFinish = null)
        {
            for (int i = 0; i < scan.Length; i++)
                scan[i] = new dbxChain();

            recover = pRecover;
            m_file = pMyDbxFile;
            m_Stream = pMyDbxFile.OpenRead();
            if (pHandlerLogMsg != null) this.PassLogMessage += pHandlerLogMsg;
            if (pHandlerProgress != null) this.PassProgress += pHandlerProgress;
            if (pHandlerFinish != null) this.PassProcessFinished += pHandlerFinish;
            dbxFileInit();
        }

        private void dbxFileInit()
        {
            int i = 0;
            uint[] signature = new uint[4];
            message_count = 0;
            capacity = 0;
            info = null;

            for (i = 0; i < 4; i++)
                signature[i] = (uint)dbxSys.sys_fread_int(m_Stream);

            if (signature[0] == 0xFE12ADCF &&
                signature[1] == 0x6F74FDC5 &&
                signature[2] == 0x11D1E366 &&
                signature[3] == 0xC0004E9A)
            {
                type = dbxEnums.dbx_type_t.DBX_TYPE_EMAIL;
            }
            else if (signature[0] == 0x36464D4A &&
                     signature[1] == 0x00010003)
            {
                type = dbxEnums.dbx_type_t.DBX_TYPE_OE4;
            }
            else if (signature[0] == 0xFE12ADCF &&
                     signature[1] == 0x6F74FDC6 &&
                     signature[2] == 0x11D1E366 &&
                     signature[3] == 0xC0004E9A)
            {
                type = dbxEnums.dbx_type_t.DBX_TYPE_FOLDER;
            }
            else
            {
                type = dbxEnums.dbx_type_t.DBX_TYPE_UNKNOWN;
            }

            if (recover)
            {
                /* we ignore file type in recovery mode */
                dbxScan();
            }
            else if (type == dbxEnums.dbx_type_t.DBX_TYPE_EMAIL)
            {

                dbxReadIndexes();
                dbxReadInfo();
                QuickSortInfo(info);
                dbxUniquifyFilenames();
            }
        }

        void dbxScan()
        {
            const int step = 4;
            int[] header = new int[8];
            int header_start = 0;
            bool ready = false;
            uint i = 0;
            uint pi = 0;
            int j = 0;
            uint di = (uint)(((uint)m_file.Length) / 1000.0);

            OnPassLogMessage("Scanning starts...");

            int MyStep = 0, MyPercent = 0, MyProgress = 0;
            if (m_file.Length < 100) {
                MyStep = 1;
                MyPercent = (int)Math.Floor((decimal)100 / (decimal)m_file.Length);
            } else {
                MyPercent = 1;
                MyStep = (int)Math.Floor((decimal)m_file.Length / (decimal)100);
            }

            List<dbxFragment>[] chains_fragmentsScan = new List<dbxFragment>[2];
            for (i = 0; i < 2; i++)
                chains_fragmentsScan[i] = new List<dbxFragment>();

            for (i = 0; i < (uint)m_file.Length; i += step)
            {
                if (i >= MyStep * ((MyProgress / MyPercent) + 1) && MyProgress < 100) {
                    MyProgress += MyPercent;
                    OnPassProgress(MyProgress);
                }
                dbxChain chains;
                dbxFragment other;
                dbxFragment fragment;
                List<dbxFragment> chains_fragments;
                if (i - pi >= di)
                {
                    pi = i;
                }

                /* initialize header buffer */
                if (!ready)
                {
                    header_start = 0;
                    for (j = 0; j < 4; j++)
                        header[j] = dbxSys.sys_fread_int(m_Stream);
                    i += 16;
                    ready = true;
                }

                header[((header_start + 4) & 7)] = dbxSys.sys_fread_int(m_Stream);
                
                /* message fragment header signature:
                                =================================
                               1st word value equals offset into file 
                               2nd word is 0x200 
                               3rd word is fragment length: must be positive, but not more than 0x200 
                               4th word value is the file offset of the next fragment, or 0 if last

                               deleted message fragment header signature:
                               =========================================
                               1st word value equals offset into file
                               2nd word is 0x1FC
                               3rd word is 0x210
                               4th word value is offset of next fragment or 0 if last
                               5th word value is offset of previous fragment (clobbering message data!)

                               sanity: we check that all offsets are less than file size,
                                and are a multiple of 4 and that fragment does not point to itself
                            */
                if (header[header_start] != i ||
                    (!(header[(header_start + 1) & 7] == 0x200 &&
                       header[(header_start + 2) & 7] > 0 &&
                       header[(header_start + 2) & 7] <= 0x200 &&
                       header[(header_start + 3) & 7] < m_file.Length &&
                       (header[(header_start + 3) & 7] & 3) == 0 &&
                       header[(header_start + 3) & 7] != i) &&
                     !(header[(header_start + 1) & 7] == 0x1FC &&
                       header[(header_start + 2) & 7] == 0x210 &&
                       header[(header_start + 3) & 7] < m_file.Length &&
                       (header[(header_start + 3) & 7] & 3) == 0 &&
                       header[(header_start + 3) & 7] != i &&
                       header[(header_start + 4) & 7] < m_file.Length &&
                       (header[(header_start + 4) & 7] & 3) == 0)))
                {
                    header_start = (header_start + 1) & 7;
                    continue;
                }

                /* add fragment to fragment list */
                chains = scan[((header[(header_start + 1) & 7] == 0x200) ? 0 : 1)];
                chains_fragments = chains_fragmentsScan[((header[(header_start + 1) & 7] == 0x200) ? 0 : 1)];
                
                while (chains_fragments.Count < chains.fragment_count + 1)
                    chains_fragments.Add(new dbxFragment());

                int fragment_id = chains.fragment_count;
                fragment = chains_fragments.ElementAt(chains.fragment_count);
                fragment.prev = -1;
                fragment.next = -1;
                fragment.offset = (uint)header[header_start];
                fragment.offset_next = (uint)header[(header_start + 3) & 7];
                fragment.size = (uint)header[(header_start + 2) & 7];
                chains.fragment_count++;
                chains.count++;

                /* find next fragment, if we already passed it, starting with the previous fragment, which is it, usually */
               
                int other_id;
                if (fragment.offset_next > 0 && fragment.offset_next < fragment.offset)
                {
                    other_id = fragment_id - 1;
                    while (other_id >= 0)
                    {
                        other = chains_fragments.ElementAt(other_id);
                        /* avoid fragments that have already been used, to avoid loops */
                        if (other.prev < 0 && fragment.offset_next == other.offset)
                        {
                            fragment.next = other_id;
                            other.prev = chains.fragment_count - 1;
                            chains.count--;
                            break;
                        }
                        other_id--;
                    }
                }

                /* find prev fragment, starting with the previous fragment, which is it, usually */
                other_id = fragment_id -1;

                while (other_id >= 0)
                {
                    other = chains_fragments.ElementAt(other_id);
                    /* avoid fragments that have already been used, to avoid loops */
                    if (other.next < 0 && fragment.offset == other.offset_next)
                    {
                        fragment.prev = other_id;
                        other.next = chains.fragment_count - 1;
                        chains.count--;
                        break;
                    }
                    
                    other_id--;
                }

                /* skip contents of fragment */
                i += 0x200 - 4;
                m_Stream.Seek(i + 20, SeekOrigin.Begin);

                /* header buffer should be refilled */
                ready = false;
            }
            scan[0].fragments = chains_fragmentsScan[0].ToArray();
            scan[1].fragments = chains_fragmentsScan[1].ToArray();
            
            /* collect the fragments that start messages chains
                         messages start with fragments where prev == -1,
                         i.e. no other fragment points to it
                     */
            for (j = 0; j < (int)dbxEnums.dbx_scan_t.DBX_SCAN_NUM; j++)
            {
                if (scan[j].count > 0)
                {
                    int nm = 0;
                    int nf = 0;

                    scan[j].chains = new dbxFragment[scan[j].count];
                    for (int x = 0; x < scan[j].chains.Length; x++)
                        scan[j].chains[x] = new dbxFragment();
                    for (nm = 0; nm < scan[j].count; nm++)
                    {
                        while (scan[j].fragments[nf].prev >= 0)
                            nf++;
                        scan[j].chains[nm] = scan[j].fragments[nf];
                        scan[j].chains[nm].ifragment = nf;
                        nf++;
                    }
                }
            }

            OnPassProcessFinished();
        }

        int dbxReadIndexes()
        {
            int index_ptr = 0;
            int item_count = 0;

            m_Stream.Seek(INDEX_POINTER, SeekOrigin.Begin);
            index_ptr = dbxSys.sys_fread_int(m_Stream);

            m_Stream.Seek(ITEM_COUNT, SeekOrigin.Begin);
            item_count = dbxSys.sys_fread_int(m_Stream);

            if (item_count > 0)
                return dbxReadIndex(index_ptr);
            else
                return 0;
        }

        int dbxReadIndex(int pos)
        {
            int i;
            int next_table = 0;
            byte ptr_count;
            int index_count = 0;

            if (pos <= 0 || (UInt64)m_file.Length <= (UInt64)pos)
            {
                OnPassLogMessage("WARNING: DBX file: " + m_file.Name + " is corrupted " +
                    "(bad seek offset " + String.Format("{0:x8}", pos) + "). " +
                    "Consider running in recovery mode.");

                return 0;
            }

            m_Stream.Seek(pos + 8, SeekOrigin.Begin);
            next_table = dbxSys.sys_fread_int(m_Stream);
            m_Stream.Seek(5, SeekOrigin.Current);
            byte[] MyByte = new byte[1];
            m_Stream.Read(MyByte, 0, MyByte.Length);
            ptr_count = MyByte[0];
            if (ptr_count <= 0)
            {
                OnPassLogMessage("WARNING: DBX file: " + m_file.Name + " is corrupted " +
                    "(bad count " + ptr_count.ToString() + " at offset " + 
                    String.Format("{0:x8}", pos+8+4+5) + "Consider running in recovery mode.");

                return 0;
            }
            m_Stream.Seek(2, SeekOrigin.Current);
            index_count = dbxSys.sys_fread_int(m_Stream);

            if (index_count > 0)
            {
                if (dbxReadIndex(next_table) < 0)
                    return 0;
            }

            pos += 24;

            info = new dbxInfo[capacity + ptr_count];
            capacity += ptr_count;
            for (i = 0; i < ptr_count; i++)
            {
                int index_ptr = 0;
                m_Stream.Seek(pos, SeekOrigin.Begin);
                index_ptr = dbxSys.sys_fread_int(m_Stream);
                next_table = dbxSys.sys_fread_int(m_Stream);
                index_count = dbxSys.sys_fread_int(m_Stream);

                info[message_count] = new dbxInfo();
                info[message_count].index = index_ptr;
                message_count++;

                pos += 12;
                if (index_count > 0)
                {
                    if (dbxReadIndex(next_table) == 0)
                        return 0;
                }
            }

            return 1;
        }

        static int dbxReadInt(Stream pStream, int offset, int value)
        {
            int val = value;
            if (offset > 0)
            {
                pStream.Seek(offset, SeekOrigin.Begin);
                val = dbxSys.sys_fread_int(pStream);
            }
            return val;
        }

        public static int strlen(string s)
        {
            string temp = s + '\0';
            char[] c = temp.ToCharArray();
            int length = 0;
            while (c[length] != '\0') length++;
            return length;
        }

        static string dbxReadString(Stream pStream, int offset)
        {

            byte[] MyByte = new Byte[255];
            pStream.Seek(offset, SeekOrigin.Begin);
            int l;
            String temp;
            StringBuilder MySBuilder = new StringBuilder();
            do
            {
                pStream.Read(MyByte, 0, MyByte.Length);
                temp = System.Text.Encoding.Default.GetString(MyByte);
                if (temp.Contains('\0') == true)
                    l = strlen(temp);
                else
                    l = 255;
                MySBuilder.Append(temp.Substring(0, l));
            } while (l == 255);
            return MySBuilder.ToString();
        }

        static DateTime dbxReadDate(Stream pStream, int offset)
        {
            Int64 filetime = 0;
            pStream.Seek(offset, SeekOrigin.Begin);
            filetime = dbxSys.sys_fread_int64(pStream);
            return DateTime.FromFileTime(filetime);
        }

        void dbxReadInfo()
        {
            int MyStep = 0, MyPercent = 0, MyProgress = 0;
            if (message_count < 100) {
                MyStep = 1;
                MyPercent = (int)Math.Floor((decimal)100 / (decimal)message_count);
            } else {
                MyPercent = 1;
                MyStep = (int)Math.Floor((decimal)message_count / (decimal)100);
            }

            for (int i = 0; i < message_count; i++)
            {
                int j;
                int size = 0;
                int count = 0;
                int index = info[i].index;
                int offset = 0;
                int pos = index + 12;

                m_Stream.Seek(index + 4, SeekOrigin.Begin);
                size = dbxSys.sys_fread_int(m_Stream);
                count = dbxSys.sys_fread_int(m_Stream);
                count = (count & 0x00FF0000) >> 16;

                info[i].valid = 0;

                for (j = 0; j < count; j++)
                {
                    int type = 0;
                    uint value = 0;

                    m_Stream.Seek(pos, SeekOrigin.Begin);
                    value = (uint)dbxSys.sys_fread_int(m_Stream);
                    type = (int)value & 0xFF;
                    value = (value >> 8) & 0xFFFFFF;

                    /* msb means direct storage */
                    offset = (int)(((type & 0x80) > 0) ? 0 : (index + 12 + 4 * count + value));

                    /* dirt ugly code follows ... */
                    switch (type & 0x7f)
                    {
                        case 0x00:
                            info[i].message_index = (uint)dbxReadInt(m_Stream, offset, (int)value);
                            info[i].valid |= dbxEnums.dbx_mask_t.DBX_MASK_INDEX;
                            break;
                        case 0x01:
                            info[i].flags = (uint)dbxReadInt(m_Stream, offset, (int)value);
                            info[i].valid |= dbxEnums.dbx_mask_t.DBX_MASK_FLAGS;
                            break;
                        case 0x02:
                            info[i].send_create_time = dbxReadDate(m_Stream, offset);
                            break;
                        case 0x03:
                            info[i].body_lines = (uint)dbxReadInt(m_Stream, offset, (int)value);
                            info[i].valid |= dbxEnums.dbx_mask_t.DBX_MASK_BODYLINES;
                            break;
                        case 0x04:
                            info[i].message_address = (uint)dbxReadInt(m_Stream, offset, (int)value);
                            info[i].valid |= dbxEnums.dbx_mask_t.DBX_MASK_MSGADDR;
                            break;
                        case 0x05:
                            info[i].original_subject = dbxReadString(m_Stream, offset);
                            break;
                        case 0x06:
                            info[i].save_time = dbxReadDate(m_Stream, offset);
                            break;
                        case 0x07:
                            info[i].message_id = dbxReadString(m_Stream, offset);
                            break;
                        case 0x08:
                            info[i].subject = dbxReadString(m_Stream, offset);
                            break;
                        case 0x09:
                            info[i].sender_address_and_name = dbxReadString(m_Stream, offset);
                            break;
                        case 0x0A:
                            info[i].message_id_replied_to = dbxReadString(m_Stream, offset);
                            break;
                        case 0x0B:
                            info[i].server_newsgroup_message_number = dbxReadString(m_Stream, offset);
                            break;
                        case 0x0C:
                            info[i].server = dbxReadString(m_Stream, offset);
                            break;
                        case 0x0D:
                            info[i].sender_name = dbxReadString(m_Stream, offset);
                            break;
                        case 0x0E:
                            info[i].sender_address = dbxReadString(m_Stream, offset);
                            break;
                        case 0x10:
                            info[i].message_priority = (uint)dbxReadInt(m_Stream, offset, (int)value);
                            info[i].valid |= dbxEnums.dbx_mask_t.DBX_MASK_MSGPRIO;
                            break;
                        case 0x11:
                            info[i].message_size = (uint)dbxReadInt(m_Stream, offset, (int)value);
                            info[i].valid |= dbxEnums.dbx_mask_t.DBX_MASK_MSGSIZE;
                            break;
                        case 0x12:
                            info[i].receive_create_time = dbxReadDate(m_Stream, offset);
                            break;
                        case 0x13:
                            info[i].receiver_name = dbxReadString(m_Stream, offset);
                            break;
                        case 0x14:
                            info[i].receiver_address = dbxReadString(m_Stream, offset);
                            break;
                        case 0x1A:
                            info[i].account_name = dbxReadString(m_Stream, offset);
                            break;
                        case 0x1B:
                            info[i].account_registry_key = dbxReadString(m_Stream, offset);
                            break;
                    }
                    pos += 4;
                }
                dbxSetFilename(info[i], i);

                if (i % MyStep == 0 && i != 0) {
                    MyProgress += MyPercent;
                    OnPassProgress(MyProgress);
                }
            }
            OnPassProcessFinished();
        }

        void dbxSetFilename(dbxInfo info, int pNumberOfMessage)
        {
            StringBuilder filename = new StringBuilder(DBX_MAX_FILENAME);
            StringBuilder suffix = new StringBuilder();

            filename.Append(info.subject.Length > 0 ? info.subject.Substring(0, (info.subject.Length > 100) ? 100 : info.subject.Length) : "(no_subject)");
        
            suffix.Append("." + ((ulong)info.receive_create_time.Ticks & (ulong)0xFFFFFFFF).ToString().Substring(0, 3));
            suffix.Append(((ulong)info.send_create_time.Ticks & (ulong)0xFFFFFFFF).ToString().Substring(0, 3));

            filename.Append(suffix);
            info.filename = dbxSanitizeFilename(filename.ToString() + ".eml");
        }

        static String dbxSanitizeFilename(string pFileName)
        {
            if (string.IsNullOrEmpty(pFileName)) return null;
            Char[] MyChars = new Char[]{'\\', '/', '?', '\"', '<', '>', '*', '"', ',', ':', '|', '\r', '\n', '\0', '\b', '\a', '\f', '\t', '\v'};
            foreach (char c in MyChars)
                pFileName = pFileName.Replace(c, ' ');
            return pFileName;
        }

        static void QuickSortInfo(dbxInfo[] pInfo)
        {
            dbxInfoComparer comparer = new dbxInfoComparer();
            Array.Sort<dbxInfo>(pInfo, comparer);
        }

        void dbxUniquifyFilenames()
        {
            int i = 1;
            int cl = ".eml".Length - 1;
            StringBuilder MySBuilder = new StringBuilder();
            while (i < message_count)
            {
                int n;

                /* skip info entries with non-equal filenames */
                while (i < message_count && info[i - 1].filename.CompareTo(info[i].filename) != 0)
                    i++;

                /* uniquify equal filename entries */
                n = 1;
                if (i < message_count)
                {
                    do
                    {
                        MySBuilder.Clear();
                        MySBuilder.Append(info[i - 1].filename.Substring(0, info[i - 1].filename.Length - cl));
                        MySBuilder.AppendFormat(".{0,8}.eml", (uint)info[i - 1].index);
                        info[i - 1].filename = MySBuilder.ToString();
                        i++;
                        n++;
                    } while (i < message_count && info[i - 1].filename.CompareTo(info[i].filename) == 0);

                    MySBuilder.Clear();
                    MySBuilder.Append(info[i - 1].filename.Substring(0, info[i - 1].filename.Length - cl));
                    MySBuilder.AppendFormat(".{0,8}.eml", (uint)info[i - 1].index);
                    info[i - 1].filename = MySBuilder.ToString();
                }
            }
        }

        public byte[] dbxMessage(int pMsgNumber, int pSize)
        {
            if (pMsgNumber >= message_count) return null;

            StringBuilder MyMessage = new StringBuilder();
            int msg_offset = 0;
            int msg_offset_ptr = 0;
            int i = 0;

            int index = info[pMsgNumber].index;
            m_Stream.Seek(index + 4, SeekOrigin.Begin);
            int size = dbxSys.sys_fread_int(m_Stream);
            m_Stream.Seek(2, SeekOrigin.Current);

            byte[] MyByte = new byte[1];
            m_Stream.Read(MyByte, 0, MyByte.Length);
            byte ptr_count = MyByte[0];

            m_Stream.Seek(1, SeekOrigin.Current);

            for (i = 0; i < ptr_count; i++)
            {
                int value = dbxSys.sys_fread_int(m_Stream);
                int type = value & 0xFF;
                value = (value >> 8) & 0xFFFFFF;

                if (type == 0x84)
                {
                    msg_offset = value;
                    break;
                }

                if (type == 0x04)
                {
                    msg_offset_ptr = index + 12 + value + 4 * ptr_count;
                    break;
                }
            }

            if (msg_offset == 0 && msg_offset_ptr != 0)
            {
                m_Stream.Seek(msg_offset_ptr, SeekOrigin.Begin);
                msg_offset = dbxSys.sys_fread_int(m_Stream);
            }

            i = msg_offset;
            
            List<byte> MyBufor = new List<byte>();
            while (i != 0)
            {
                m_Stream.Seek(i + 8, SeekOrigin.Begin);
                short block_size = dbxSys.sys_fread_short(m_Stream);
                if (block_size <= 0 || block_size > 0x200)
                {
                    OnPassLogMessage("WARNING: DBX file: " + m_file.Name + " is corrupted " +
                        "(bad block size " + String.Format("{0:x4}",block_size) + " at offset " +
                        String.Format("{0:x8}", i + 8) + "). Consider running in recovery mode.");
                    
                    break;
                }

                m_Stream.Seek(2, SeekOrigin.Current);
                i = dbxSys.sys_fread_int(m_Stream);
            
                byte[] MyByte2 = new byte[block_size];
                m_Stream.Read(MyByte2, 0, block_size);

                for (int t = 0; t < block_size; t++)
                    MyBufor.Add(MyByte2[t]);
            }

            return MyBufor.ToArray();
        }

        public byte[] dbxRecoverMessage(int pChainIndex, int pMsgNumber,out DateTime pTimeStamp,out int pSize,out string pFileName)
        {
            List<byte> MyMessage = new List<byte>();
            int size = 0;
            string filename = string.Empty;
            DateTime timestamp = new DateTime();
            dbxFragment pfragment = null;
            int ifragment = this.scan[pChainIndex].chains[pMsgNumber].ifragment;
            uint fsize = 0;

         
            string suffix = string.Empty;
            byte[] msgSubject = new byte[1];
            byte[] msgTo = new byte[1];
            byte[] msgFrom = new byte[1];

           
            for (; ifragment >= 0; ifragment = pfragment.next)
            {
                pfragment = scan[pChainIndex].fragments[ifragment];
                /* deleted fragments have size 0x210, which is wrong - it's 0x200 */
                fsize = pfragment.size <= 0x200 ? pfragment.size : 0x200;
                m_Stream.Seek(pfragment.offset + 16,SeekOrigin.Begin);
                byte[] MyBuffor = new Byte[fsize];   
                m_Stream.Read(MyBuffor,0,MyBuffor.Length);
               
                for (int t = 0; t < MyBuffor.Length; t++)
                {
                    if (pChainIndex != 0 && t<4)
                        MyMessage.Add(BitConverter.GetBytes('-')[0]);
                    else
                        MyMessage.Add(MyBuffor[t]);
                }
                /* each deleted fragment starts with bad 4 bytes
                                (it's set to the offset of the previous fragment)
                                so we replace them with 4 dashes, which eases
                                eml parsing and should at least make the text readable
                            */
                    
                size += (int)fsize;
            }

            if (MyMessage.Count > 0)
            {
                if (pChainIndex > 0)
                {
                    int zeros = 0;
                    while (zeros <= size && MyMessage.ElementAt((size-1) - zeros) == 0)
                        zeros++;
                    if (zeros > 0)
                        size -= zeros - 1;
                }
                emlRead.EmlParse(MyMessage.ToArray(), ref msgSubject, ref msgFrom, ref msgTo, ref timestamp);
            } 

            String Subject = (msgSubject != null) ? Encoding.Default.GetString(msgSubject) : null;
            Subject = (Subject != null) ? Subject.Substring(0, (Subject.Length > 100) ? 100 : Subject.Length) : "(no_subject)";

            filename += Subject;

            suffix = "." + string.Format("{0:x8}", scan[pChainIndex].chains[pMsgNumber].offset) + ".eml";

            filename += suffix;
            pFileName = dbxSanitizeFilename(filename);
            pTimeStamp = timestamp;
            pSize = size;
            return MyMessage.ToArray();
        }

        private void OnPassLogMessage(string e)
        {
            if (PassLogMessage != null) PassLogMessage(e);
        }

        private void OnPassProgress(int e)
        {
            if (PassProgress != null) PassProgress(e);
        }

        private void OnPassProcessFinished()
        {
            if (PassProcessFinished != null) PassProcessFinished();
        }

        public FileInfo m_file;
        bool recover;
        public dbxEnums.dbx_type_t type;
        public int message_count;
        int capacity;
        public dbxInfo[] info;
        public dbxChain[] scan = new dbxChain[(int)dbxEnums.dbx_scan_t.DBX_SCAN_NUM];
        Stream m_Stream;
    }
}
